带有弯曲路径动画的 svg 动画 marker-end/marker-start
svg animate marker-end/marker-start with curved path animation
我有使用 animate
元素设置动画的曲线路径。它确实结合 @keyframes
对路径进行动画处理,但是当我在弯曲路径内添加标记时,标记不会进行动画处理。我通过更改路径值属性使用简单路径测试了一个。它使用简单的路径完美运行,但我如何让标记元素与弯曲路径一起动画?
codepen 演示:https://codepen.io/tfss/pen/yZoBLo 使用简单和弯曲的路径
由于您拥有的是贝塞尔曲线,因此您可以计算从原点到新位置绘制曲线的点。要了解接下来会发生什么,您需要了解什么是贝塞尔曲线。
在下一个示例中,我将使用输入类型范围来更改曲线。您可以将其设置为动画。请阅读代码中的注释。
//the points used to draw the final curve
let points = [[308.7, 34.9],[381.3, 37.4],[444.3, 78],[478.7,137.5]];
//the position of the final point of the actual curve on the final curve.
let t = 0.5;
//the points for the actual curve
let newPoints = getBezierPoints(t);
drawCBezier(newPoints, the_bezier);
// on input recalcalculate the points for the curve and the curve
T.addEventListener("input", function() {
t = this.value;
newPoints = getBezierPoints(t);
drawCBezier(newPoints, the_bezier);
});
function getBezierPoints(t) {
let helperPoints = [];
// helper points 0,1,2
for (let i = 1; i < 4; i++) {
//points.length must be 4 !!!
let p = lerp(points[i - 1], points[i], t);
helperPoints.push(p);
}
// helper points 3,4
helperPoints.push(lerp(helperPoints[0], helperPoints[1], t));
helperPoints.push(lerp(helperPoints[1], helperPoints[2], t));
// helper point 5 is where the first Bézier ends and where the second Bézier begins
helperPoints.push(lerp(helperPoints[3], helperPoints[4], t));
// points for the dynamic bézier
let firstBezier = [
points[0],
helperPoints[0],
helperPoints[3],
helperPoints[5]
];
return firstBezier;
}
function lerp(A, B, t) {
// a virtual line from A to B
// get the position of a point on this line
// if(t == .5) the point in in the center of the line
// 0 <= t <= 1
let ry = [
(B[0] - A[0]) * t + A[0], //x
(B[1] - A[1]) * t + A[1] //y
];
return ry;
}
function drawCBezier(points, path) {
let d = `M${points[0][0]},${points[0][1]} C`;
// points.length == 4
for (let i = 1; i < 4; i++) {
d += ` ${points[i][0]},${points[i][1]}`;
}
path.setAttributeNS(null, "d", d);
}
svg {
border: 1px solid;
width:100vh
}
<input type="range" value=".5" min="0" max="1" step=".01" id="T">
<svg viewBox="290 0 200 150">
<defs>
<marker
id="arrow"
orient="auto-start-reverse"
viewBox="0 0 7.1 11.5"
markerWidth="7.1"
markerHeight="11.5"
markerUnits="strokeWidth"
refX="5" refY="5.75">
<path d="M1 11.5L0 10.4L5.1 5.7L0 1L1 0L7.1 5.7L1 11.5" fill="#00897b"></path>
</marker>
<marker id="circle" viewBox="0 0 6 6" refX="1" refY="3"
markerUnits="userSpaceOnUse" orient="auto"
markerWidth="6" markerHeight="6">
<circle cx="3" cy="3" r="3" fill="#4caf50"/>
</marker>
</defs>
<path id="the_bezier" d=""
marker-start="url(#circle)"
marker-end="url(#arrow)"
stroke-width="2"
fill="none"
stroke="blue" />
</svg>
您可能会发现 post 关于贝塞尔曲线的这篇文章很有用:How to add a point to a Cubic Bézier Curve In SVG
对于不同的方法,您可能喜欢阅读此答案:描边动画,如何将另一条路径附加到出现的描边?
希望对您有所帮助。
我有使用 animate
元素设置动画的曲线路径。它确实结合 @keyframes
对路径进行动画处理,但是当我在弯曲路径内添加标记时,标记不会进行动画处理。我通过更改路径值属性使用简单路径测试了一个。它使用简单的路径完美运行,但我如何让标记元素与弯曲路径一起动画?
codepen 演示:https://codepen.io/tfss/pen/yZoBLo 使用简单和弯曲的路径
由于您拥有的是贝塞尔曲线,因此您可以计算从原点到新位置绘制曲线的点。要了解接下来会发生什么,您需要了解什么是贝塞尔曲线。
在下一个示例中,我将使用输入类型范围来更改曲线。您可以将其设置为动画。请阅读代码中的注释。
//the points used to draw the final curve
let points = [[308.7, 34.9],[381.3, 37.4],[444.3, 78],[478.7,137.5]];
//the position of the final point of the actual curve on the final curve.
let t = 0.5;
//the points for the actual curve
let newPoints = getBezierPoints(t);
drawCBezier(newPoints, the_bezier);
// on input recalcalculate the points for the curve and the curve
T.addEventListener("input", function() {
t = this.value;
newPoints = getBezierPoints(t);
drawCBezier(newPoints, the_bezier);
});
function getBezierPoints(t) {
let helperPoints = [];
// helper points 0,1,2
for (let i = 1; i < 4; i++) {
//points.length must be 4 !!!
let p = lerp(points[i - 1], points[i], t);
helperPoints.push(p);
}
// helper points 3,4
helperPoints.push(lerp(helperPoints[0], helperPoints[1], t));
helperPoints.push(lerp(helperPoints[1], helperPoints[2], t));
// helper point 5 is where the first Bézier ends and where the second Bézier begins
helperPoints.push(lerp(helperPoints[3], helperPoints[4], t));
// points for the dynamic bézier
let firstBezier = [
points[0],
helperPoints[0],
helperPoints[3],
helperPoints[5]
];
return firstBezier;
}
function lerp(A, B, t) {
// a virtual line from A to B
// get the position of a point on this line
// if(t == .5) the point in in the center of the line
// 0 <= t <= 1
let ry = [
(B[0] - A[0]) * t + A[0], //x
(B[1] - A[1]) * t + A[1] //y
];
return ry;
}
function drawCBezier(points, path) {
let d = `M${points[0][0]},${points[0][1]} C`;
// points.length == 4
for (let i = 1; i < 4; i++) {
d += ` ${points[i][0]},${points[i][1]}`;
}
path.setAttributeNS(null, "d", d);
}
svg {
border: 1px solid;
width:100vh
}
<input type="range" value=".5" min="0" max="1" step=".01" id="T">
<svg viewBox="290 0 200 150">
<defs>
<marker
id="arrow"
orient="auto-start-reverse"
viewBox="0 0 7.1 11.5"
markerWidth="7.1"
markerHeight="11.5"
markerUnits="strokeWidth"
refX="5" refY="5.75">
<path d="M1 11.5L0 10.4L5.1 5.7L0 1L1 0L7.1 5.7L1 11.5" fill="#00897b"></path>
</marker>
<marker id="circle" viewBox="0 0 6 6" refX="1" refY="3"
markerUnits="userSpaceOnUse" orient="auto"
markerWidth="6" markerHeight="6">
<circle cx="3" cy="3" r="3" fill="#4caf50"/>
</marker>
</defs>
<path id="the_bezier" d=""
marker-start="url(#circle)"
marker-end="url(#arrow)"
stroke-width="2"
fill="none"
stroke="blue" />
</svg>
您可能会发现 post 关于贝塞尔曲线的这篇文章很有用:How to add a point to a Cubic Bézier Curve In SVG
对于不同的方法,您可能喜欢阅读此答案:描边动画,如何将另一条路径附加到出现的描边?
希望对您有所帮助。